{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "71aa43d6-98c9-4552-a9cd-d65784eef882",
   "metadata": {},
   "outputs": [],
   "source": [
    "# File: noodle.ipynb\n",
    "# Code: Claude Code and Codex\n",
    "# Review: Ryoichi Ando (ryoichi.ando@zozo.com)\n",
    "# License: Apache v2.0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "33a15148-7429-4f33-8445-9487b0d9180e",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "from frontend import App\n",
    "\n",
    "# create an app\n",
    "app = App.create(\"noodle\")\n",
    "\n",
    "# create a 1D rod strand mesh (long vertical line)\n",
    "V, E = app.mesh.line([0, 0.01, 0], [0.01, 15, 0], 960)\n",
    "app.asset.add.rod(\"strand\", V, E)\n",
    "\n",
    "# create a scene\n",
    "scene = app.scene.create()\n",
    "\n",
    "# add invisible hemisphere bowl to collect noodles\n",
    "scene.add.invisible.sphere([0, 1, 0], 1.1).invert().hemisphere()\n",
    "\n",
    "# add grid of noodle strands falling into bowl\n",
    "N, scale = 11, 0.05\n",
    "for i, j in np.ndindex((N, N)):\n",
    "    x, y = scale * (i - N / 2), scale * (j - N / 2)\n",
    "    obj = scene.add(\"strand\").at(x, 0, y).jitter()\n",
    "    # set material properties for flexible noodles\n",
    "    (\n",
    "        obj.param.set(\"bend\", 10)\n",
    "        .set(\"contact-gap\", 3e-3)\n",
    "        .set(\"contact-offset\", 3e-3)\n",
    "        .set(\"friction\", 0.05)\n",
    "    )\n",
    "\n",
    "# set preview options for close-up view\n",
    "opts = {\"lookat\": [0, 0.25, 0], \"eyeup\": 1.0, \"fov\": 4}\n",
    "\n",
    "# compile the scene and report stats\n",
    "scene = scene.build().report()\n",
    "\n",
    "# preview the initial scene\n",
    "scene.preview(options=opts)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fed21b38-09ee-4557-8d19-6c413ab5872c",
   "metadata": {},
   "outputs": [],
   "source": [
    "# create a new session with the compiled scene\n",
    "session = app.session.create(scene)\n",
    "\n",
    "# set session parameters with air friction to slow falling\n",
    "(\n",
    "    session.param.set(\"frames\", 240)\n",
    "    .set(\"fix-xz\", 1.0)\n",
    "    .set(\"isotropic-air-friction\", 1e-5)\n",
    ")\n",
    "\n",
    "# build this session\n",
    "session = session.build()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a0b681a5-02ca-4211-acb6-51ca3aa74363",
   "metadata": {},
   "outputs": [],
   "source": [
    "# start the simulation and live-preview the results\n",
    "session.start().preview(options=opts)\n",
    "\n",
    "# also show simulation logs in realtime\n",
    "session.stream()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "06dfc048-e0c5-49a5-b2b8-b9ff26d08766",
   "metadata": {},
   "outputs": [],
   "source": [
    "# create an animation from the simulation results\n",
    "session.animate(options=opts)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1e4e3a0e-abda-4695-900d-48db50b03f34",
   "metadata": {},
   "outputs": [],
   "source": [
    "# export the animation to file\n",
    "session.export.animation()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c8e2432d-7328-4d73-afbb-159ae5cff76d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# this is for CI\n",
    "if app.ci:\n",
    "    assert session.finished()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
